#!usr/bin/ruby
# coding: utf-8

require "socket"
require "open-uri"

class LolBot
  def initialize server, channel, port, nick
    @@OPLIST = "OPlist.txt"
    @@TROLLFACE = "trollface.txt"
    @server = server
    @channel = channel
    @port = port
    @nick = nick

    @owner = "tobijoh"
    @basic_commands = "help | opList"
    @op_commands = "#{@basic_commands} | op <nick1> [<nick2>] | exit | nithander | sing [<song>]"
    
    @song_url = "http://tobijoh.se/LolBot/upload_song/"
    @i_should_sing = true
    
    # Read in the OP list
    @op_list = Array.new
    file = File.new "OPlist.txt", "r"
      while line = file.gets
        @op_list << line.strip
      end
    file.close
    @op_list.sort
  end

  def connect_to_server
    @irc_socket = TCPSocket.open @server, @port
    send_raw_message "NICK #{@nick}"
    send_raw_message "USER #{@nick} \"\" \"\" :#{@nick} :: tobijoh's slave"
  end

  def join_channel
    puts "JOINING #{@channel}"
    send_raw_message "JOIN #{@channel}"
  end

  def parse_message message
    puts message
    case message.strip
      when /^PING :(.+)$/i
        on_ping $1
      when /^:(.+)!(.+) JOIN :#{@channel}$/i
        on_join $1
      when /^:(.+)!(.+) PRIVMSG #{@channel} :!(.+)$/i
        parsed_message = $3.split
        command = parsed_message.shift
        arguments = parsed_message
        handle_command $1, command, arguments
    end
  end
  
  def on_ping ping_message
    puts "[SERVER PING]"
    send_raw_message "PONG :#{ping_message}"
    get_nicks.each do |nick| 
      op nick if @op_list.include? nick and !nick.start_with? "@"
    end
  end
  
  def on_join source_nick
    op source_nick if @op_list.include? source_nick
  end

  def handle_command source_nick, command, arguments
    case command
      when "say";         on_say source_nick, arguments.join(' ')
      when "private";     on_private_message source_nick, arguments.shift, arguments.join(' ')
      when "exit";        on_exit source_nick
      when "help";        on_help source_nick
      when "nithander";   on_nithander source_nick
      when "opList";      on_op_list
      when "op";          multi_op source_nick, arguments
      when "addOp";       on_add_op source_nick, arguments
      when "removeOp";    on_remove_op source_nick, arguments
      when "sing";        on_sing source_nick, arguments
      when "stopSinging"; on_stop_singing source_nick
      else
        send_channel_message "#{source_nick}: That's not an available command. Try one of these instead:"
        send_channel_message (@op_list.include? source_nick) ? @op_commands : @basic_commands
    end
  end

  def on_exit source_nick
    send_channel_message source_nick == @owner ? "Goodbye, master #{@owner}!" : "#{source_nick}: Lolwut?"
    if source_nick == @owner
      send_raw_message "QUIT #{@channel}"
      write_list_to_file @@OPLIST, @op_list
      @irc_socket.close
      @irc_socket = nil
    end
  end
  
  def on_say source_nick, arguments
    send_channel_message arguments if source_nick == @owner
  end
  
  def on_private_message source_nick, target_nick, arguments
    send_private_message target_nick, arguments if source_nick == @owner
  end
  
  def on_help source_nick
    send_channel_message "#{source_nick}: Available commands:"
    send_channel_message (@op_list.include? source_nick) ? @op_commands : @basic_commands
  end
  
  def on_nithander source_nick
    Thread.new {
      file = File.new "trollface.txt", "r"
      while line = file.gets
        send_channel_message line
        sleep 0.6
      end
      file.close
    } if @op_list.include? source_nick
  end
  
  def on_op_list
    send_channel_message @op_list.join(", ")
  end
  
  def on_sing source_nick, song
    unless @op_list.include? source_nick
        send_channel_message "#{source_nick}: I don't have to obey you!"
	return
    end
    # The parameter song is actually an array, so we have to shift it.
    song = song.shift
    puts "Song: #{song}"
    if get_song_list.include? song
      Thread.new {
        @i_should_sing = true
        file = open "#{@song_url}/songs/#{song}.txt"
        while @i_should_sing and line = file.gets
          send_channel_message "♪ #{line}"
          sleep 0.8
        end
        @i_should_sing = false
      }
    else
      send_channel_message "#{source_nick}: Hmm, I don't know that song :-/"
      send_channel_message "#{source_nick}: Try one of these instead:"
      send_channel_message get_song_list.join ", "
      send_channel_message "Or add your own song at #{@song_url}"
    end
  end
  
  def on_stop_singing source_nick
    @i_should_sing = false if @op_list.include? source_nick
  end
  
  def get_song_list
    songs = open("#{@song_url}/songs").readline.split
    formatted_songs = Array.new
    songs.each do |song|
      # Remove file extension from song name
      formatted_songs << File.basename(song, '.*')
    end
    return formatted_songs
  end
  
  def get_nicks
    send_raw_message "NAMES #{@channel}"
    if @irc_socket.gets.match(/^:#{@server} 353 (.+) :(.+)$/i)
      puts "People in channel: #{$2}"
      return $2.split
    end
  end
  
  def on_add_op source_nick, nicks
    nicks.each do |nick|
      if !@op_list.include? nick
        @op_list << nick 
        send_channel_message "#{source_nick}: Added #{nick} to the OP list!"
      else
        send_channel_message "#{source_nick}: #{nick} is already on the OP list!"
      end
    end if @op_list.include? source_nick
    @op_list.sort
  end
  
  def on_remove_op source_nick, nicks
    nicks.each do |nick|
      if @op_list.delete nick
        send_channel_message "#{source_nick}: Removed #{nick} from the OP list!"
      else
        send_channel_message "#{source_nick}: #{nick} is not on the OP list!"
      end
    end if @op_list.include? source_nick
    @op_list.sort
  end
  
  def multi_op source_nick, nicks
    nicks.each do |nick| 
      op nick
    end if @op_list.include? source_nick
  end
  
  def op nick
    send_raw_message "MODE #{@channel} +o :#{nick}"
  end

  def send_channel_message message
    puts ">> #{message}"
    send_raw_message "PRIVMSG #{@channel} :#{message}"
  end

  def send_private_message target_nick, message
    puts ">> #{target_nick}: #{message}"
    send_raw_message "PRIVMSG #{target_nick} :#{message}"
  end

  def send_raw_message message
    @irc_socket.send("#{message}\n", 0)
  end
  
  def write_list_to_file file_name, list
    File.open file_name, 'w' do |f| 
      f.write list.join "\n" 
    end
  end

  def main
    while @irc_socket
      s = @irc_socket.gets
      parse_message s
    end
  end
end

# lolbot = LolBot.new "irc.homelien.no", "#tobijoh", 6667, "botijoh"
lolbot = LolBot.new ARGV[0], "##{ARGV[1]}", 6667, "botijoh"
lolbot.connect_to_server
lolbot.join_channel

begin
  lolbot.main
rescue Interrupt
rescue Exception => detail
  puts detail.message
retry
end
